package com.beiding.hotcode;


import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/*
    一个大号容器
 */

public class PackageGroup {

    //所有的包
    private Set<String> allClasses = new HashSet<>();

    private Set<HotPackage> hotPackages = new HashSet<>();

    //扫描的时候只扫描最外层的
    private Set<HotPackage> outermostHotPackages = new HashSet<>();

    private Map<String, JavaMonitor> javaMonitorMap = new HashMap<>();

    private Map<HotPackage, MonitorGroup> giveBackMap = new HashMap<>();

    //

    private MonitorGroup monitorGroup;

    private MyFactory myFactory;

    private PackageGroup() {

    }

    public synchronized void add(MyPackage myPackage) {

        if (myPackage == null) {
            return;
        }

        if (myPackage instanceof HotPackage) {
            HotPackage hotPackage = (HotPackage) myPackage;

            if (hotPackage.myFactory != this.myFactory) {
                throw new RuntimeException("不是相同工厂创建");
            }

            synchronized (hotPackage) {
                if (!this.hotPackages.contains(hotPackage)) {

                    giveBackMap.put(hotPackage, hotPackage.monitorGroup);

                    hotPackage.monitorGroup = this.monitorGroup;

                    for (JavaMonitor value : hotPackage.javaMonitorMap.values()) {
                        value.setMonitorGroup(this.monitorGroup);
                    }

                    //所有被它包含的都要移除
                    for (HotPackage aPackage : hotPackages) {
                        if (hotPackage.include(aPackage)) {
                            outermostHotPackages.remove(aPackage);
                        }
                    }

                    //判断是否有包含它的
                    boolean flag = true;
                    for (HotPackage outermostHotPackage : outermostHotPackages) {
                        if (outermostHotPackage.include(hotPackage)) {
                            flag = false;
                            break;
                        }
                    }

                    //如果没有将添加到最外层
                    if (flag) {
                        outermostHotPackages.add(hotPackage);
                    }
                    this.hotPackages.add(hotPackage);
                }
            }
        }
    }


    public synchronized void remove(MyPackage myPackage) {

        if (myPackage == null) {
            return;
        }

        if (myPackage instanceof HotPackage) {
            HotPackage hotPackage = (HotPackage) myPackage;

            synchronized (hotPackage) {
                if (this.hotPackages.contains(hotPackage)) {

                    hotPackage.monitorGroup = giveBackMap.remove(hotPackage);

                    //归还分组
                    for (JavaMonitor value : hotPackage.javaMonitorMap.values()) {
                        value.setMonitorGroup(hotPackage.monitorGroup);
                    }


                    this.hotPackages.remove(hotPackage);
                    this.outermostHotPackages.remove(hotPackage);
                }
            }
        }
    }

    static PackageGroup create(MyFactory myFactory) {
        PackageGroup packageGroup = new PackageGroup();
        packageGroup.myFactory = myFactory;
        packageGroup.monitorGroup = new MonitorGroup(myFactory) {
            @Override
            boolean checkChange() {

                Set<String> set = new HashSet<>();
                for (HotPackage aPackage : packageGroup.outermostHotPackages) {
                    set.addAll(aPackage.scanJavaFile());
                }
                if (!set.equals(packageGroup.allClasses)) {

                    Set<String> willRemove = new HashSet<>(packageGroup.allClasses);
                    willRemove.removeAll(set);

                    for (String s : willRemove) {
                        JavaMonitor remove = packageGroup.javaMonitorMap.remove(s);
                        remove(remove);
                    }

                    Set<String> willAdd = new HashSet<>(set);
                    willAdd.removeAll(packageGroup.allClasses);
                    for (String s : willAdd) {
                        JavaMonitor javaMonitor = myFactory.getMonitor(s);
                        packageGroup.javaMonitorMap.put(s, javaMonitor);
                        add(javaMonitor);
                    }
                    packageGroup.allClasses = set;
                }

                return super.checkChange();

            }
        };

        return packageGroup;
    }

}
